﻿using CPAIS.Model;
using System;
using System.Xml;
using System.Collections.Generic;

namespace CPAIS.LogParser.PluginsWithXmlReader
{
    /// <summary>
    /// Log parser for parsing event log file with XML Root TAG 'Eventlog', like:
    /// 
    /// <EventLogGeneratedByPIES Encoding="No">
    ///     <PatientTrace PatientID="101750" VisitID="11" PatientName="" AdmitDate="2007-09-17T08:00:00Z" DichargeDate="2007-09-20T08:00:00Z">
    ///         <Observation OccurTime="2007-09-18T08:37:30Z" Type="LabTest">
    ///             <Name>乙肝表面抗原</Name>
    ///             <English>hepatitis b surface antigen</English>
    ///             <Result Unit="S/CO">阴性</Result>
    ///             <Abnormal>N</Abnormal>
    ///         </Observation>
    ///     </PatientTrace>
    /// </EventLogGeneratedByPIES>
    /// 
    /// </summary>
    public class LogParserD : ILogParserWithXmlReader
    {
        #region String used for parsing XML file
        private const string EventLogGeneratedByPIES = "EventLogGeneratedByPIES";
        private const string PatientTrace = "PatientTrace";
        private const string VisitID = "VisitID";
        private const string PatientID = "PatientID";
        private const string AdmitDate = "AdmitDate";
        private const string DischargeDate = "DischargeDate";
        private const string BirthDay = "BirthDay";
        private const string Gender = "Gender";
        private const string Department = "Department";
        private const string Cost = "Cost";
        private const string Observation = "Observation";
        private const string MedicalIntervention = "MedicalIntervention";
        private const string Name = "Name";
        private const string English = "English";

        private const string OccurTime = "OccurTime";

        private const string Event = "Event";
        private const string ID = "ID";
        private const string Day = "Day";

        private const string Male = "男";
        private const string Female = "女";
        private const char Observ = 'O';
        private const char Action = 'A';

        private const string KnowledgeBase = "KnowledgeBase";
        private const string Context = "Context";
        private const string Id_Index = "Id_Index";
        private const string Type = "Type";
        private const string Col_Name = "Col_Name";
        #endregion

        private class KnowledgeBaseItem:IEquatable<KnowledgeBaseItem>
        {
            public string Id_Index;
            public string Type;
            public string ID;
            public string Col_Name;

            public bool Equals(KnowledgeBaseItem other)
            {
                if (this.ID == other.ID && this.Type == other.Type && this.Col_Name == other.Col_Name)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        private Dictionary<string, KnowledgeBaseItem> knowledgeBase;

        public LogParserD()
        {
            knowledgeBase = new Dictionary<string, KnowledgeBaseItem>();
        }

        public string XmlTag
        {
            get { return EventLogGeneratedByPIES; }
        }

        public void Parse(Model.LogModel logModel, System.Xml.XmlReader reader, Updater.StateUpdater updater)
        {
            while (reader.Read())
            {
                #region Depth 1
                if (reader.Depth < 1)
                    break;
                //No need to read knowlegeBase
                //else if (reader.Depth == 1 && reader.NodeType == XmlNodeType.Element && reader.Name == KnowledgeBase)
                //{
                //    while (reader.Read())
                //    {
                //        if (reader.Depth < 2)
                //            break;
                //        else if (reader.Depth == 2 && reader.NodeType == XmlNodeType.Element && reader.Name == Context)
                //        {
                //            KnowledgeBaseItem item = new KnowledgeBaseItem();
                //            while (reader.MoveToNextAttribute())
                //            {
                //                if (reader.Name == Id_Index)
                //                    item.Id_Index = reader.ReadContentAsString().Trim();
                //                else if (reader.Name == Type)
                //                    item.Type = reader.ReadContentAsString().Trim();
                //                else if (reader.Name == ID)
                //                    item.ID = reader.ReadContentAsString().Trim();
                //                else if (reader.Name == Col_Name)
                //                    item.Col_Name = reader.ReadContentAsString().Trim();
                //            }
                //            knowledgeBase.Add(item.Id_Index, item);
                //        }
                //    }
                //}
                else if (reader.Depth == 1 && reader.NodeType == XmlNodeType.Element && reader.Name == PatientTrace)
                {
                    #region Read Patient Attributes

                    string
                        strVistID = "",
                        strPatientID = "",
                        strSex = "",
                        strDepartment = "",
                        strCost = "";
                    DateTime
                        admitTime = new DateTime(),
                        dischargeTime = new DateTime(),
                        birthday = new DateTime();

                    while (reader.MoveToNextAttribute())
                    {
                        switch (reader.Name)
                        {
                            case PatientID:
                                strPatientID = reader.ReadContentAsString().Trim();
                                break;
                            case VisitID:
                                strVistID = reader.ReadContentAsString().Trim();
                                break;
                            case Gender:
                                strSex = reader.ReadContentAsString().Trim();
                                break;
                            case Department:
                                strDepartment = reader.ReadContentAsString().Trim();
                                break;
                            case Cost:
                                strCost = reader.ReadContentAsString().Trim();
                                break;
                            case AdmitDate:
                                admitTime = reader.ReadContentAsDateTime();
                                break;
                            case DischargeDate:
                                dischargeTime = reader.ReadContentAsDateTime();
                                break;
                            case BirthDay:
                                birthday = reader.ReadContentAsDateTime();
                                break;
                        }
                    }
                    Trace trace = new Trace(logModel, strPatientID + "_" + strVistID);
                    trace.Sex = strSex == Male ? SexType.Male : (strSex == Female ? SexType.Female : SexType.Unknown);
                    trace.Department = strDepartment;
                    trace.Cost = strCost == "" ? 0.0 : double.Parse(strCost);
                    trace.Admit = admitTime;
                    trace.Discharge = dischargeTime;
                    trace.Birthday = birthday;

                    #endregion

                    while (reader.Read())
                    {
                        #region Depth 2
                        if (reader.Depth < 2)
                            break;
                        else if (reader.Depth == 2 && reader.NodeType == XmlNodeType.Element)
                        {
                            if (reader.Name == Observation || reader.Name == MedicalIntervention)
                            {
                                string nodeType = reader.Name;
                                DateTime occurTime = new DateTime();
                                while (reader.MoveToNextAttribute())
                                {
                                    switch (reader.Name)
                                    {
                                        case OccurTime:
                                            occurTime = Convert.ToDateTime(reader.ReadContentAsString().Replace("T", " ").Replace("Z", ""));
                                            break;
                                    }
                                }
                                string name = "";
                                string english = "";
                                while (reader.Read())
                                {
                                    #region Depth 3
                                    if (reader.Depth < 3)
                                        break;
                                    else if (reader.Depth == 3 && reader.NodeType == XmlNodeType.Element)
                                    {                                       
                                        if (reader.Name == Name)
                                        {
                                            name = reader.ReadString();
                                        }
                                        else if (reader.Name == English)
                                        {
                                            english = reader.ReadString();
                                        }
                                    }
                                    #endregion
                                }
                                KnowledgeBaseItem item = new KnowledgeBaseItem();
                                item.ID = name;
                                item.Col_Name = english;
                                if (nodeType == MedicalIntervention)
                                {
                                    item.Type = "Action";
                                }
                                else if (nodeType == Observation)
                                {
                                    item.Type = "Observation";
                                }
                                if (!knowledgeBase.ContainsValue(item))
                                {
                                    if (nodeType == MedicalIntervention)
                                    {
                                        item.Id_Index = "A" + knowledgeBase.Count;
                                    }
                                    else if (nodeType == Observation)
                                    {
                                        item.Id_Index = "O" + knowledgeBase.Count;
                                    }
                                    knowledgeBase.Add(item.Id_Index, item);
                                }
                                int day = (occurTime - trace.Admit).Days;
                                if (nodeType == Observation)
                                {
                                    ObservationEvent entry = new ObservationEvent(trace, day, name);
                                    trace.AddObservation(entry);
                                }
                                else if (nodeType == MedicalIntervention)
                                {
                                    ActionEvent entry = new ActionEvent(trace, day, name);
                                    trace.AddAction(entry);
                                }
                            }
                        }
                        #endregion
                    }
                    if (knowledgeBase.ContainsKey("A0"))
                        trace.AddAction(new ActionEvent(trace, 0, knowledgeBase["A0"].ID));
                    else
                        trace.AddAction(new ActionEvent(trace, 0, "A0"));
                    trace.OrderTrace();
                    logModel.AddTrace(trace);
                    if (updater != null)
                        updater.UpdateMessage(string.Format("{0} traces have been loaded...", logModel.TraceCount));
                }
                #endregion
            }
        }
    }
}